/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include <stdint.h>
#include <stdio.h>
#include <rpc/types.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#include "hash.h"
#include "common.h"

static bool_t ischar_inset(char, const char *);

char *
copy_string(const char *s)
{
    if (s == (char *)NULL) {
        s = "";
    }

    char *p;
    p = (char *)malloc(strlen(s)+1);
    if (!p) {
        log_message(ERR, NOCLASS, "%s: calloc(%d) failed: %m", __func__, strlen(s)+1);
        abort();
    }

    strcpy(p, s);

    return p;
}

int
askfor_confirm(char *msg)
{
    char answer[PATH_MAX];
    int i = 0;

    while (1) {
        fputs(msg, stdout);
        fflush(stdout);

        if (fgets(answer, PATH_MAX, stdin) == NULL) {
            return FALSE;
        }

        i = 0;
        while (answer[i] == ' ')
            i++;

        if ((answer[i] == 'y' || answer[i] == 'n' || answer[i] == 'Y' || answer[i] == 'N')
            && answer[i+1] == '\n') {
            break;
        }
    }

    return ((answer[i] == 'Y' || answer[i] == 'y'));
}

char *
get_string_token(char **line, const char *set, char *word, size_t size)
{
    char *lv_word = word;

    while (isspace(**line)) {
        (*line)++;
    }

    while(ischar_inset(**line, set)) {
        (*line)++;
    }

    while (**line && !ischar_inset(**line, set)) {
        size--;
        if (size == 0) {
            return NULL;
        }
        *lv_word++ = *(*line)++;
    }
    *lv_word = '\0';

    if (lv_word == word) {
        return NULL;
    }

    return word;
}

char *
get_string_inside(char **line, char ch1, char ch2)
{
    char str[4+1];

    unsigned int len = strlen(*line);
    char *instr = (char *)calloc(len+1, sizeof(char));
    char *value = (char *)calloc(len+1, sizeof(char));
    if (NULL == instr || NULL == value) {
        log_message(ERR, NOCLASS, "%s: calloc(%d) failed: %m", __func__, len);
        abort();
        return NULL;
    }

    char *token;
    token = get_string_token(line, " ", instr, len+1);
    if (NULL == token) {
        FREEUP(instr);
        FREEUP(value);
        return NULL;
    }

    if (token[0] != ch1) {
        FREEUP(value);
        return token;
    }

    char *cp;
    snprintf(str, 4, "%c", ch1);
    if (strcmp(token, str) != 0) {

        token++;
        strip_spaces(token, FALSE);
        strncpy(value, token, len);

        cp = strchr(value, ch2);
        if (cp != NULL) {
            *cp = '\0';
            FREEUP(instr);
            return strip_spaces(value, FALSE);
        }
    }

    snprintf(str, 4, "%c", ch2);

    len = strlen(*line);
    instr[0] = '\0';
    while ((token = get_string_token(line, " ", instr, len+1)) != NULL) {
        len = strlen(*line);

        if (strcmp(token, str) == 0) {
            FREEUP(instr);
            return value;
        }

        if (0 == strlen(value)) {
            strcpy(value, token);
        } else {
            strcat(value, " ");
            strcat(value, token);
        }

        cp = strrchr(value, ch2);
        if (cp != NULL) {
            *cp = '\0';
            FREEUP(instr);
            return value;
        }
    }
    FREEUP(instr);
    FREEUP(value);

    return NULL;
}

bool_t
has_name_inlist(char *name,  char **list, int listsize)
{
    if (0 == listsize) {
        return FALSE;
    }

    int i;
    for(i = 0; i < listsize; i++) {

        if (NULL == list[i]) {
            return FALSE;
        }

        if (0 == strcmp(name, list[i])) {
            return TRUE;
        }
    }

    return FALSE;
}

int
strip_quotes(char *q, char *str)
{
    char *fr = q;

    for (; *q != '"' && *q != '\0'; q++);

    if (*q == '\0') {
        return -1;
    }

    for (q++; *q != '\0'; q++, str++) {
        if (*q == '"') {
            if (*(q+1) == '"') {
                q++;
            } else {
                *str = '\0';
                break;
            }
        }

        *str = *q;
    }

    if (*q == '\0') {
        return -1;
    }

    return q - fr + 1;
}

int
add_quotes(FILE *log_fp, char *str)
{
    int j = 1;

    if (putc (' ', log_fp) == EOF)
        return -1;
    if (putc ('"', log_fp) == EOF)
        return -1;
    for (; *str != '\0'; str++, j++) {
        if (*str == '"')
            if (putc ('"', log_fp) == EOF)
                return -1;
        if (putc (*str, log_fp) == EOF)
            return -1;
    }
    if (putc ('"', log_fp) == EOF)
        return -1;

    return 0;
}

char *
strip_allspaces(const char *str)
{
    if (str == NULL) {
        return NULL;
    }

    int l;
    l = strlen(str);

    char *s;
    s = (char *)calloc(l + 1, sizeof(char));
    if (NULL == s) {
        return NULL;
    }

    int cc;
    for (cc = 0; cc < l; cc++) {
        if (str[cc] == ' ' || str[cc] == '\t') {
            ++cc;
            continue;
        }

        s[cc] = str[cc];
    }

    return s;
}

char *
strip_spaces(char *field, bool_t star)
{
    char *cp;

    cp = field;
    while (*cp == ' ') {
        cp++; //trim left spaces
    }

    if (star && *cp == '*') { //trim spaces after *
        char *sp;

        sp = cp;
	for (sp=sp+1; *sp==' '; sp++) {
	    *(sp-1) = ' ';
	    *sp = '*';
	}
    }

    unsigned int len, i;
    len = strlen(field);
    if (0 == len) {
        return cp;
    }

    i = len - 1;
    while((i > 0) && (field[i] == ' ')) {
        i--; //trim right spaces
    }

    if (i < len-1) {
        field[i + 1] = '\0';
    }

    return(cp);
}

void
strip_newline(char* instr)
{
    int i, k, len;

    if (instr == NULL || (len = strlen(instr)) == 0) {
        return;
    }

    for (i = len-1; i >= 0; i--) {
        if( instr[i] != '\n' ) {
            continue;
        }

        for(k = i; k < len; k++) {
            instr[k] = instr[k+1];
        }
    }

    return;
}

int
gf_setenv(const char *env, char *value)
{
    //overwrite if the environment variable is already set
    return setenv(env, value, 1);
}

char *
ctime_2string(int format, const time_t *timer, char *timeStr)
{
    struct tm localtime;

    ctime_r(timer, timeStr);
    switch (format) {
    case CTIME_FORMAT_a_b_d_T_Y:
        localtime_r(timer, &localtime);
        snprintf(timeStr, TIMESTRINGLEN, "%.4d/%.2d/%.2d %.2d:%.2d:%.2d", 
                 localtime.tm_year + 1900, localtime.tm_mon+1,
                localtime.tm_mday, localtime.tm_hour,
                localtime.tm_min, localtime.tm_sec);
        return (timeStr);
    case CTIME_FORMAT_a_b_d_T:
        timeStr[19]='\0';
        return (timeStr);
    case CTIME_FORMAT_b_d_H_M:
        timeStr[16]='\0';
        return (timeStr+4);
    case CTIME_FORMAT_m_d_Y:
        localtime_r(timer, &localtime);
        snprintf(timeStr, TIMESTRINGLEN, "%.2d/%.2d/%.4d", 
                localtime.tm_mon+1, localtime.tm_mday,
                localtime.tm_year + 1900);
        return (timeStr);
    case CTIME_FORMAT_H_M_S:
        localtime_r(timer, &localtime);
        snprintf(timeStr,  TIMESTRINGLEN, "%.2d:%.2d:%.2d", 
                localtime.tm_hour, localtime.tm_min, localtime.tm_sec);
        return (timeStr);
    case CTIME_FORMAT_DEFAULT:
    default:
	return (timeStr);
    }
} 

char *
cputimer_2string(float timer, char *str)
{
    int Hour, Minute, Second, Point, Time;

    Point   = timer*100.0;
    Point   = Point%100;
    Time    = timer;
    Hour    = Time/3600;
    Minute  = (Time%3600)/60;
    Second  = (Time%3600)%60;

    snprintf(str, TIMESTRINGLEN, "%03d:%02d:%02d.%02d", Hour, Minute, Second, Point);

    return str;
} // end function cputimer_2string

struct nameList *
new_namelist(unsigned int numstr)
{
    struct nameList *nameList;
    nameList = (struct nameList *)calloc(1, sizeof(struct nameList));
    if (NULL == nameList) {
        return NULL;
    }

    nameList->names = (char **)calloc(numstr, sizeof(char *));
    if (NULL == nameList->names) {
        FREEUP(nameList);
        return NULL;
    }
    nameList->size = numstr;

    return nameList;
}

struct nameList *
merge_namelist(struct nameList *tolist, struct nameList *fromlist)
{
    if (0 == fromlist->num_name) {
        return tolist;
    }

    unsigned int i;
    for (i=0; i<fromlist->num_name; i++) {
        if (0 > add_member_2namelist(fromlist->names[i], tolist)) {
            return NULL;
        }
    }

    return tolist;
}

struct nameList *
string_2namelist(char **strlist, unsigned int numstr)
{
    if (numstr <= 0 || NULL == strlist) {
        return NULL;
    }

    struct nameList *nameList;
    nameList = new_namelist(numstr);
    if (NULL == nameList) {
        return NULL;
    }

    HASH_TABLE_T *stringHash;
    stringHash = gf_hash_make(numstr);

    unsigned int i;
    for (i=0; i<numstr; i++) {

        if (NULL == strlist[i]) {
            continue;
        }

        if (NULL != gf_hash_find(stringHash, strlist[i])) {
            continue;
        }

        gf_hash_install(stringHash, strlist[i], (void *)(strlist[i]));

        nameList->names[nameList->num_name] = copy_string(strlist[i]);
        nameList->num_name ++;
    }

    gf_hash_free(stringHash, NULL);

    return nameList;
}

struct nameList *
longstring_2namelist(char *inputstr)
{
    unsigned int numStr = strlen(inputstr)/2 + 1;

    struct nameList *nameList;
    nameList = new_namelist(numStr);
    if (NULL == nameList) {
        return NULL;
    }

    HASH_TABLE_T *stringHash;
    stringHash = gf_hash_make(numStr);

    char *sp = inputstr;
    unsigned int len = strlen(sp);
    char *name = (char *)calloc(len+1, sizeof(char));
    if (NULL == name) {
        return NULL;
    }

    char *token;
    while ((token = get_string_token(&sp, " ", name, len+1)) != NULL) {
        len = strlen(sp);

        if (NULL != gf_hash_find(stringHash, token)) {
            continue;
        }

        if (nameList->num_name == numStr) {

            numStr *= 2;

            char **newNames;
            newNames = (char **)realloc(nameList->names, numStr*sizeof(char *));
            if (NULL == newNames) {
                unsigned int i;

                for(i = 0; i < nameList->num_name; i++) {
                    FREEUP(nameList->names[i]);
                }

                FREEUP(nameList->names);
                FREEUP(nameList);
                gf_hash_free(stringHash, NULL);

                return NULL;
            }

            nameList->names = newNames;
            nameList->size = numStr;
        }

        gf_hash_install(stringHash, token, (void *)token);

        nameList->names[nameList->num_name] = copy_string(token);
        nameList->num_name ++;
    }

    FREEUP(name);
    gf_hash_free(stringHash, NULL);

    return nameList;
}

int
add_member_2namelist(char *member, struct nameList *namel)
{
    unsigned int i;
    for (i=0; i<namel->num_name; i++) {
        if (0 == strcmp(member, namel->names[i])) {
            break;
        }
    }

    if (i < namel->num_name) {
        return 0;
    }

    if (namel->num_name == namel->size) {

        int numStr = namel->size;
        numStr *= 2;

        char **newNames;
        newNames = (char **)realloc(namel->names, numStr*sizeof(char *));
        if (NULL == newNames) {
            return -1;
        }

        namel->names = newNames;
        namel->size = numStr;
    }

    namel->names[namel->num_name] = copy_string(member);
    namel->num_name ++;

    return 0;
}

char *
namelist_2string(struct nameList *namel)
{
    unsigned int i, len=0;
    for (i=0; i<namel->num_name; i++) {
        len += strlen(namel->names[i]) + 1;
    }

    char *strNames;
    if (0 == len) {
        strNames = copy_string("");
        return strNames;
    }

    strNames = (char *)calloc(len, sizeof(char));
    if (NULL == strNames) {
        return NULL;
    }

    for (i=0; i<namel->num_name; i++) {
        if (0 == i) {
            strcpy(strNames, namel->names[i]);
        } else {
            strcat(strNames, " ");
            strcat(strNames, namel->names[i]);
        }
    }

    return strNames;
}

void
free_namelist(struct nameList *namearray)
{
    if (NULL == namearray || NULL == namearray->names) {
        return;
    }

    unsigned int i;
    for (i=0; i < namearray->num_name; i++) {
        FREEUP(namearray->names[i]);
    }

    FREEUP(namearray->names);
    FREEUP(namearray);

    return;
}

void
free_string_array(char **list, int num)
{
    int i;

    if (NULL == list || 0 >= num) {
        return;
    }

    for (i=0; i<num; i++) {
        FREEUP(list[i]);
    }

    FREEUP (list);

    return;
}

char *
gf_get_line(FILE *file, unsigned int *linenum, char *linecontent)
{
    char *sp = NULL;
    while (NULL != fgets(linecontent, BUFSIZ, file)) {

        sp = linecontent;
        sp = strip_spaces(sp, FALSE);
        if (*sp == '\n' || *sp == '#' || (*sp == '/' && *(sp++) == '/')
            || (*sp == '/' && *(sp++) == '*') || (*sp == '*' && *(sp++) == '/')
            || *sp == '*' || *sp == '\0') { 

            (*linenum) ++;
            continue;
        }

        char *p = strstr(sp, "//");
        if (NULL != p) {

            *p = '\0';
            sp = strip_spaces(sp, FALSE);

            if (*sp == '\0') {

                (*linenum) ++;
                continue;
            }
        }

        (*linenum) ++;
        break;
    }

    return sp;
}

bool_t
isinteger(const char *word)
{
    long number;

    if (!word || *word == '\0') {
        return FALSE;
    }

    errno = 0;

    number = strtol (word, (char **)&word, 10);
    if (*word == '\0' &&  errno != ERANGE) {
        if (number <= INFINIT_INT && number > -INFINIT_INT) {
            return TRUE;
        }
    }

    return FALSE;
}

bool_t
isnumber(const char *word)
{
    double number;

    if (!word || *word == '\0') {
        return FALSE;
    }

    if (errno == ERANGE) {
        errno = 0;
    }

    number = strtod(word, (char **)&word);
    if (*word == '\0' &&  errno != ERANGE) {
        if (number <= INFINIT_FLOAT && number > -INFINIT_FLOAT) {
            return TRUE;
        }
    }

    return FALSE;
}

bool_t
isdigit_string(char *string)
{
    unsigned int i;

    for (i = 0; i < strlen(string); i++) {
        if (! isdigit(string[i])) {
            return FALSE;
        }
    }

    return TRUE;
}

bool_t
islong(char *word)
{
    long long int number;

    if (!word || *word == '\0') {
        return FALSE;
    }

    if(!isdigit_string(word)) return FALSE;

    if (errno == ERANGE) {
        errno = 0;
    }

    sscanf(word, "%lld", &number);
    if (errno != ERANGE) {
        if (number <= INFINIT_LONG && number > -INFINIT_LONG) {
            return TRUE;
        }
    }

    return FALSE;
}

int
gf_atoi(char *strint, int top, int bottom)
{
    long num;

    if (!isinteger(strint)) {
        return (INFINIT_INT);
    }

    num = atoi(strint);
    if (num >= top || num <= bottom)
        return (INFINIT_INT);

    return (num);
}

float
gf_atof(char *strfloat, float top, float bottom)
{
    double num;

    if (!isnumber(strfloat)) {
        return (INFINIT_FLOAT);
    }

    num = atof(strfloat);
    if (num >= top || num <= bottom) {
        return (INFINIT_FLOAT);
    }

    return (num);
}


#define ISBOUNDARY(h1, h2, len) ((h1[len]=='.' || h1[len]=='\0') && \
                                 (h2[len]=='.' || h2[len]=='\0'))

bool_t
issame_host(const char *host1, const char *host2)
{
    int len;

    if (strlen(host1) > strlen(host2)) {
        len = strlen(host2);
    } else {
        len = strlen(host1);
    }

    if ((strncasecmp(host1, host2, len) == 0) && ISBOUNDARY(host1, host2, len)) {
        return TRUE;
    }

    return FALSE;
}

static bool_t
ischar_inset(char c, const char *set)
{
    while (*set != '\0') {
	if (c == *set) {
	    return TRUE;
	}

	set++;
    }

    return FALSE;
}
